'''
Created on Jul 6, 2010

@author: riche
'''

from ft.framework import connector

class UnimplementedAbstractionError(Exception):
    '''Thrown when an unimplemented port is sent a message'''
    
class UnknownPortError(Exception):
    '''Thrown when the requested port name is not part of the abstraction.'''
    
class NotAComponentError(Exception):
    '''Thrown when a non-component is added to an abstraction'''

class Component(object):
    '''This is the interface for things that are inside containers.'''
    
    def run_component(self, port, msg):
        '''Execute the handler associated with port on msg.'''
        
    def get_endpoint(self, port_name):
        '''Return the endpoint associated with this port.'''
        
    def get_name(self):
        '''Return the name of the component'''

class Implementation(Component):
    '''
    A Implementation is the basic unit of computation. Implementation instances
    can have multiple Port instances. If a Port is an input Port, a corresponding
    handler method must be defined in the derived class. 
    '''


    def __init__(self, name):
        '''
        Constructor
        '''
        self._name = name
        self._ports = {}
        self._out_queue =[] 
        
    def get_name(self):
        '''
        Returns the Implementation name.
        '''
        return self._name
    
    def add_port(self, port):
        '''
        Adds a Port to the Implementation.
        '''
        self._ports[port.get_name()] = port
        
    def get_port(self, portName):
        '''
        Looks up the Port with the name portName.
        '''
        return self._ports[portName]
        
    def run_component(self, port, message):
        '''
        Executes the message by looking up the port, and passing 
        the message to the handler.
        '''
        try:
            self.__getattribute__(self._ports[port].get_handler())(message)
        except RestartComponent:
            return True
        else:
            return False
        
    def send_out(self, exitPortName, msg):
        '''
        Put a message on the out_queue to be sent to the next 
        component in the application.
        '''
        self._out_queue.append((exitPortName, msg))
        
    def get_enqueued_msgs(self):
        '''
        Return the list of messages to be sent out.
        '''
        return self._out_queue
    
    def clear_enqueued_msgs(self):
        '''
        Clears the enqueued messages queue.
        ''' 
        self._out_queue = []
    
    def send_kill_msg(self):
        '''
        Enqueues a message into the enqueued messages queue that will force the 
        CompThread to terminate.
        '''
        self._out_queue.append(('__KILL__', '__KILL__'))
    
    def get_dummy_conn(self):
        '''
        Placeholder method. Raises an exception if called and not defined in the child.
        '''
        raise NoConnGeneration()
    
    def get_endpoint(self, port_name):
        '''Return the endpoint associated with the given port.'''
        if port_name not in self._ports.keys():
            raise UnknownPortError(port_name)
        return connector.Endpoint(self, self._ports[port_name])
        
class NoConnGeneration(Exception):
    pass
        
class Port(object):
    '''
    Represents the input/output interface of a Implementation instance. 
    '''
    
    def __init__(self, name, handler):
        '''
        Constructor
        '''
        self._name = name
        if(handler == "__OUT__"):
            self.direction = self.__handler__ = handler
        else:
            self.__handler__ = handler
            self.direction = "__IN__"
        
    def get_name(self):
        '''
        Returns the Port name.
        '''
        return self._name
    
    def get_handler(self):
        '''
        Returns the name of the function that handles requests.
        '''
        return self.__handler__
    
class RestartComponent(Exception):
    pass